import { NextRequest, NextResponse } from 'next/server';
import { withImmediateTransaction } from '@/lib/db';
import { evaluateMatch } from '@/lib/match-logic';
import { resolveSponsorToRestaurantId } from '@/lib/sponsor-resolver';
import { parseBody, swipeSchema } from '@/lib/validation';
import type { SessionRow, ParticipantRow } from '@/lib/db-types';

class ApiError extends Error {
  constructor(
    public status: number,
    public body: object,
  ) {
    super();
  }
}

export async function POST(req: NextRequest, { params }: { params: Promise<{ code: string }> }) {
  const { code } = await params;
  const parsed = await parseBody(req, swipeSchema);
  if (!parsed.success) return parsed.response;

  const { participantId, restaurantId, direction } = parsed.data;

  // Sponsor-Karten kommen als "sponsor-<id>" vom Frontend. Da swipes/matches per
  // Foreign Key auf restaurants(id) zeigen, wird beim ersten Swipe automatisch ein
  // echter restaurants-Eintrag angelegt (oder wiederverwendet), damit die normale
  // Swipe-/Match-Logik unveraendert weiterlaufen kann.
  const resolvedRestaurantId = resolveSponsorToRestaurantId(restaurantId);
  if (resolvedRestaurantId === null) {
    return NextResponse.json({ error: 'Restaurant nicht gefunden' }, { status: 404 });
  }

  try {
    const result = withImmediateTransaction((db) => {
      const session = db.prepare('SELECT * FROM sessions WHERE code = ?').get(code) as SessionRow | undefined;
      if (!session) {
        throw new ApiError(404, { error: 'Session nicht gefunden' });
      }

      const participant = db.prepare(
        'SELECT * FROM participants WHERE id = ? AND session_id = ? AND is_active = 1'
      ).get(participantId, session.id) as ParticipantRow | undefined;
      if (!participant) {
        throw new ApiError(403, { error: 'Teilnehmer nicht berechtigt' });
      }

      if (direction === 'veto') {
        if (participant.vetos_used >= 3) {
          throw new ApiError(400, { error: 'Keine Vetos mehr verfuegbar' });
        }
        db.prepare(
          'INSERT OR IGNORE INTO vetos (session_id, participant_id, restaurant_id) VALUES (?, ?, ?)'
        ).run(session.id, participantId, resolvedRestaurantId);
        db.prepare('UPDATE participants SET vetos_used = vetos_used + 1 WHERE id = ?').run(participantId);
        return { vetoed: true };
      }

      db.prepare(`
        INSERT INTO swipes (session_id, participant_id, restaurant_id, direction)
        VALUES (?, ?, ?, ?)
        ON CONFLICT(session_id, participant_id, restaurant_id) DO UPDATE SET direction = excluded.direction
      `).run(session.id, participantId, resolvedRestaurantId, direction);

      // Nach JEDEM Swipe (auch "no") neu auswerten: seit dem Quorum-Update
      // (alle aktiven Teilnehmer muessen abgestimmt haben) kann auch ein
      // "no" den Match ausloesen, wenn damit die letzte fehlende Stimme
      // eintrifft (z.B. 2x ja + 1x nein bei 3 Teilnehmern).
      const matchResult = evaluateMatch(db, session.id, resolvedRestaurantId);

      return { swiped: true, matchResult };
    });

    return NextResponse.json(result);
  } catch (e) {
    if (e instanceof ApiError) {
      return NextResponse.json(e.body, { status: e.status });
    }
    throw e;
  }
}
