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

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

// Validation schema for updating match result
const updateMatchSchema = z.object({
  score1: z.array(z.number().int().min(0)).optional(),
  score2: z.array(z.number().int().min(0)).optional(),
  winnerId: z.string().cuid('Invalid winner ID').optional(),
  status: z.enum(['SCHEDULED', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED', 'WALKOVER']).optional(),
  courtId: z.string().cuid('Invalid court ID').optional().nullable(),
  scheduledAt: z.string().datetime().optional().nullable(),
  notes: z.string().max(500).optional().nullable(),
});

// Advance winner to next round
async function advanceWinner(
  tx: Omit<typeof db, '$connect' | '$disconnect' | '$on' | '$transaction' | '$use' | '$extends'>,
  tournamentId: string,
  currentRound: number,
  currentPosition: number,
  winnerPlayers: string[]
): Promise<void> {
  const nextRound = currentRound + 1;
  const nextPosition = Math.ceil(currentPosition / 2);

  // Find the next match
  const nextMatch = await tx.match.findFirst({
    where: {
      tournamentId,
      round: nextRound,
      position: nextPosition,
    },
  });

  if (!nextMatch) {
    // No next match means this was the final
    return;
  }

  // Determine if this match feeds into team1 or team2 of next match
  // Odd positions go to team1, even positions go to team2
  const isUpperBracket = currentPosition % 2 === 1;

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

// Check if tournament is complete
async function checkTournamentComplete(
  tx: Omit<typeof db, '$connect' | '$disconnect' | '$on' | '$transaction' | '$use' | '$extends'>,
  tournamentId: string
): Promise<boolean> {
  // Find the final match (highest round)
  const finalMatch = await tx.match.findFirst({
    where: { tournamentId },
    orderBy: { round: 'desc' },
  });

  if (!finalMatch) return false;

  // Check if final match is completed
  return finalMatch.status === 'COMPLETED';
}

// PUT /api/tournaments/[id]/matches/[matchId] - Update match result
export async function PUT(
  request: NextRequest,
  context: RouteContext
) {
  try {
    const session = await getServerSession(authOptions);

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

    const { id, matchId } = await context.params;

    // Verify tournament exists and belongs to user's organization
    const tournament = await db.tournament.findFirst({
      where: {
        id,
        organizationId: session.user.organizationId,
      },
    });

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

    // Check tournament is in progress
    if (tournament.status !== 'IN_PROGRESS') {
      return NextResponse.json(
        { error: 'Tournament is not in progress' },
        { status: 400 }
      );
    }

    // Verify match exists
    const existingMatch = await db.match.findFirst({
      where: {
        id: matchId,
        tournamentId: id,
      },
    });

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

    // Can't update already completed matches
    if (existingMatch.status === 'COMPLETED') {
      return NextResponse.json(
        { error: 'Cannot update a completed match' },
        { status: 400 }
      );
    }

    const body = await request.json();

    // Validate input
    const validationResult = updateMatchSchema.safeParse(body);
    if (!validationResult.success) {
      return NextResponse.json(
        {
          error: 'Invalid match data',
          details: validationResult.error.flatten().fieldErrors,
        },
        { status: 400 }
      );
    }

    const { score1, score2, winnerId, status, courtId, scheduledAt, notes } = validationResult.data;

    // Validate winner is one of the teams
    if (winnerId && status === 'COMPLETED') {
      const team1HasWinner = existingMatch.team1Players.includes(winnerId);
      const team2HasWinner = existingMatch.team2Players.includes(winnerId);

      if (!team1HasWinner && !team2HasWinner) {
        return NextResponse.json(
          { error: 'Winner must be a player from one of the teams' },
          { status: 400 }
        );
      }
    }

    // Validate court if provided
    if (courtId) {
      const court = await db.court.findFirst({
        where: {
          id: courtId,
          site: {
            organizationId: session.user.organizationId,
          },
        },
      });

      if (!court) {
        return NextResponse.json(
          { error: 'Court not found or does not belong to your organization' },
          { status: 404 }
        );
      }
    }

    // Build update data
    interface MatchUpdateData {
      team1Score?: number[];
      team2Score?: number[];
      winnerId?: string | null;
      status?: 'SCHEDULED' | 'IN_PROGRESS' | 'COMPLETED' | 'CANCELLED' | 'WALKOVER';
      courtId?: string | null;
      scheduledAt?: Date | null;
      startedAt?: Date | null;
      completedAt?: Date | null;
      notes?: string | null;
    }

    const updateData: MatchUpdateData = {};

    if (score1 !== undefined) updateData.team1Score = score1;
    if (score2 !== undefined) updateData.team2Score = score2;
    if (winnerId !== undefined) updateData.winnerId = winnerId;
    if (status !== undefined) {
      updateData.status = status;

      // Set timestamps based on status
      if (status === 'IN_PROGRESS' && existingMatch.status === 'SCHEDULED') {
        updateData.startedAt = new Date();
      } else if (status === 'COMPLETED') {
        updateData.completedAt = new Date();
      }
    }
    if (courtId !== undefined) updateData.courtId = courtId;
    if (scheduledAt !== undefined) updateData.scheduledAt = scheduledAt ? new Date(scheduledAt) : null;
    if (notes !== undefined) updateData.notes = notes;

    // Use transaction to update match and advance winner
    const result = await db.$transaction(async (tx) => {
      // Update the match
      const updatedMatch = await tx.match.update({
        where: { id: matchId },
        data: updateData,
        include: {
          court: {
            select: {
              id: true,
              name: true,
            },
          },
        },
      });

      // If match is completed with a winner, advance to next round
      if (status === 'COMPLETED' && winnerId) {
        const winnerTeam = existingMatch.team1Players.includes(winnerId)
          ? existingMatch.team1Players
          : existingMatch.team2Players;

        await advanceWinner(
          tx,
          id,
          existingMatch.round,
          existingMatch.position,
          winnerTeam
        );

        // Check if tournament is complete
        const isComplete = await checkTournamentComplete(tx, id);
        if (isComplete) {
          await tx.tournament.update({
            where: { id },
            data: { status: 'COMPLETED' },
          });
        }
      }

      return updatedMatch;
    });

    // Fetch tournament status after update
    const updatedTournament = await db.tournament.findUnique({
      where: { id },
      select: { status: true },
    });

    return NextResponse.json({
      ...result,
      tournamentStatus: updatedTournament?.status,
    });
  } catch (error) {
    console.error('Error updating match:', error);
    return NextResponse.json(
      { error: 'Failed to update match' },
      { status: 500 }
    );
  }
}

// GET /api/tournaments/[id]/matches/[matchId] - Get match details
export async function GET(
  request: NextRequest,
  context: RouteContext
) {
  try {
    const session = await getServerSession(authOptions);

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

    const { id, matchId } = await context.params;

    // Verify tournament exists and belongs to user's organization
    const tournament = await db.tournament.findFirst({
      where: {
        id,
        organizationId: session.user.organizationId,
      },
    });

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

    const match = await db.match.findFirst({
      where: {
        id: matchId,
        tournamentId: id,
      },
      include: {
        court: {
          select: {
            id: true,
            name: true,
            type: true,
          },
        },
        booking: {
          select: {
            id: true,
            startTime: true,
            endTime: true,
            status: true,
          },
        },
      },
    });

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

    // Enrich with player details
    const allPlayerIds = [...match.team1Players, ...match.team2Players];
    const players = await db.client.findMany({
      where: {
        id: { in: allPlayerIds },
      },
      select: {
        id: true,
        firstName: true,
        lastName: true,
        level: true,
      },
    });

    const playerMap = new Map(players.map(p => [p.id, p]));

    const team1 = match.team1Players.map(id => playerMap.get(id) || { id, firstName: 'Unknown', lastName: '', level: null });
    const team2 = match.team2Players.map(id => playerMap.get(id) || { id, firstName: 'Unknown', lastName: '', level: null });

    return NextResponse.json({
      ...match,
      team1Details: team1,
      team2Details: team2,
    });
  } catch (error) {
    console.error('Error fetching match:', error);
    return NextResponse.json(
      { error: 'Failed to fetch match' },
      { status: 500 }
    );
  }
}
