import { Router, Response } from 'express';
import { z } from 'zod';
import { queryOne, queryMany, exec, withTransaction } from '../db/pool';
import { authenticate, AuthenticatedRequest } from '../middleware/auth';
import { computeShowScore } from '../lib/genetics';
import { logger } from '../lib/logger';
import { v4 as uuidv4 } from 'uuid';

const router = Router();
router.use(authenticate);

const SHOW_REWARDS: Record<string, Record<number, number>> = {
  rookie: { 1: 100, 2: 60,  3: 30  },
  pro:    { 1: 400, 2: 200, 3: 100 },
  elite:  { 1: 1500,2: 750, 3: 300 },
};

// ─── GET /events/shows ────────────────────────────────────────────────────────

router.get('/shows', async (req: AuthenticatedRequest, res: Response) => {
  const { tier, status = 'open' } = req.query;
  const conditions: string[] = ['1=1'];
  const params: unknown[] = [];

  if (tier)   { conditions.push('s.tier = ?');   params.push(tier); }
  if (status) { conditions.push('s.status = ?'); params.push(status); }

  try {
    const rows = await queryMany<any>(
      `SELECT s.*, COUNT(se.id) as entry_count
       FROM shows s LEFT JOIN show_entries se ON se.show_id = s.id
       WHERE ${conditions.join(' AND ')}
       GROUP BY s.id ORDER BY s.entry_closes_at ASC LIMIT 50`,
      params
    );
    res.json(rows.map(s => ({
      id: s.id, name: s.name, tier: s.tier, entryFee: s.entry_fee,
      entryClosesAt: s.entry_closes_at, judgedAt: s.judged_at,
      status: s.status, seasonId: s.season_id, entryCount: parseInt(s.entry_count),
    })));
  } catch (err) {
    logger.error('GET /events/shows error', err);
    res.status(500).json({ error: 'INTERNAL_ERROR' });
  }
});

// ─── GET /events/shows/:id ────────────────────────────────────────────────────

router.get('/shows/:id', async (req: AuthenticatedRequest, res: Response) => {
  const { id } = req.params;
  try {
    const show = await queryOne<any>('SELECT * FROM shows WHERE id = ?', [id]);
    if (!show) return res.status(404).json({ error: 'NOT_FOUND' });

    const entries = await queryMany<any>(`
      SELECT se.*, u.username as player_username,
             a.name as animal_name, a.breed, a.sex, a.stage, a.rarity_tier
      FROM show_entries se
      JOIN users   u ON u.id = se.player_id
      JOIN animals a ON a.id = se.animal_id
      WHERE se.show_id = ?
      ORDER BY se.rank ASC, se.created_at ASC
    `, [id]);

    res.json({
      id: show.id, name: show.name, tier: show.tier, entryFee: show.entry_fee,
      entryClosesAt: show.entry_closes_at, judgedAt: show.judged_at,
      status: show.status, seasonId: show.season_id,
      entries: entries.map(e => ({
        id: e.id, playerId: e.player_id, playerUsername: e.player_username,
        animalId: e.animal_id, animalName: e.animal_name, breed: e.breed,
        rarityTier: e.rarity_tier,
        score: e.score ? parseFloat(e.score) : null,
        rank: e.rank, rewardClaimed: !!e.reward_claimed,
      })),
    });
  } catch (err) {
    logger.error('GET /events/shows/:id error', err);
    res.status(500).json({ error: 'INTERNAL_ERROR' });
  }
});

// ─── POST /events/shows/:id/enter ────────────────────────────────────────────

router.post('/shows/:id/enter', async (req: AuthenticatedRequest, res: Response) => {
  const { id: showId } = req.params;
  const parsed = z.object({ animalId: z.string().uuid() }).safeParse(req.body);
  if (!parsed.success) return res.status(400).json({ error: 'VALIDATION_ERROR' });

  const { animalId } = parsed.data;
  const userId = req.user!.id;

  try {
    await withTransaction(async (client) => {
      const [[showRows]] = await client.execute(
        `SELECT * FROM shows WHERE id = ? AND status = 'open' AND entry_closes_at > NOW() FOR UPDATE`,
        [showId]
      ) as any[];
      const s = (showRows as any[])[0];
      if (!s) throw { statusCode: 404, error: 'NOT_FOUND', message: 'Show not found or entry closed' };

      const [[animalRows]] = await client.execute(
        'SELECT * FROM animals WHERE id = ? AND owner_id = ? AND died_at IS NULL',
        [animalId, userId]
      ) as any[];
      const a = (animalRows as any[])[0];
      if (!a) throw { statusCode: 404, error: 'NOT_FOUND', message: 'Animal not found' };
      if (a.status === 'pregnant') throw { statusCode: 400, error: 'INVALID', message: 'Pregnant animals cannot enter shows' };
      if (a.stage === 'calf') throw { statusCode: 400, error: 'INVALID', message: 'Calves cannot enter shows' };

      const [[dupRows]] = await client.execute(
        'SELECT id FROM show_entries WHERE show_id = ? AND animal_id = ?',
        [showId, animalId]
      ) as any[];
      if ((dupRows as any[]).length) throw { statusCode: 409, error: 'DUPLICATE', message: 'Animal already entered' };

      if (s.entry_fee > 0) {
        const [deduct] = await client.execute(
          'UPDATE users SET gold_balance = gold_balance - ? WHERE id = ? AND gold_balance >= ?',
          [s.entry_fee, userId, s.entry_fee]
        ) as any[];
        if ((deduct as any).affectedRows === 0) throw { statusCode: 400, error: 'INSUFFICIENT_GOLD', message: `Entry fee: ${s.entry_fee} Gold` };

        const [[userRow]] = await client.execute('SELECT gold_balance FROM users WHERE id = ?', [userId]) as any[];
        await client.execute(
          `INSERT INTO transactions (id, user_id, amount, currency, category, balance_after, description, ref_id)
           VALUES (?, ?, ?, 'gold', 'fee', ?, ?, ?)`,
          [uuidv4(), userId, -s.entry_fee, (userRow as any[])[0].gold_balance, `Show entry: ${s.name}`, showId]
        );
      }

      await client.execute(
        'INSERT INTO show_entries (id, show_id, player_id, animal_id) VALUES (?, ?, ?, ?)',
        [uuidv4(), showId, userId, animalId]
      );

      res.status(201).json({ success: true, message: `${a.name} entered in ${s.name}!`, entryFee: s.entry_fee });
    });
  } catch (err: any) {
    if (err.statusCode) return res.status(err.statusCode).json({ error: err.error, message: err.message });
    logger.error('Show entry error', err);
    res.status(500).json({ error: 'INTERNAL_ERROR' });
  }
});

// ─── POST /events/shows/:id/judge ────────────────────────────────────────────

router.post('/shows/:id/judge', async (req: AuthenticatedRequest, res: Response) => {
  const { id: showId } = req.params;

  try {
    await withTransaction(async (client) => {
      const [[showRows]] = await client.execute(
        `SELECT * FROM shows WHERE id = ? AND status = 'open' FOR UPDATE`,
        [showId]
      ) as any[];
      const s = (showRows as any[])[0];
      if (!s) throw { statusCode: 404, error: 'NOT_FOUND', message: 'Show not open' };

      const [entries] = await client.execute(`
        SELECT se.*, ap.weight_score, ap.milk_yield_score, ap.growth_rate_score,
               ap.temperament_score, ap.coat_quality_score, ap.fertility_score,
               ap.hardiness_score, ap.show_potential_score,
               a.\`condition\`, a.health
        FROM show_entries se
        JOIN animals a ON a.id = se.animal_id
        JOIN animal_phenotypes ap ON ap.animal_id = a.id
        WHERE se.show_id = ?
      `, [showId]) as any[];

      const scored = (entries as any[]).map((entry: any) => {
        const phenotype = {
          weightScore: parseFloat(entry.weight_score), milkYieldScore: parseFloat(entry.milk_yield_score),
          growthRateScore: parseFloat(entry.growth_rate_score), temperamentScore: parseFloat(entry.temperament_score),
          coatQualityScore: parseFloat(entry.coat_quality_score), fertilityScore: parseFloat(entry.fertility_score),
          hardinessScore: parseFloat(entry.hardiness_score), showPotentialScore: parseFloat(entry.show_potential_score),
        };
        return { ...entry, score: computeShowScore(phenotype, parseFloat(entry.condition), showId, entry.animal_id, false) };
      });

      scored.sort((a, b) => b.score - a.score);
      scored.forEach((e, i) => { e.rank = i + 1; });

      for (const entry of scored) {
        await client.execute('UPDATE show_entries SET score = ?, `rank` = ? WHERE id = ?', [entry.score, entry.rank, entry.id]);
      }

      await client.execute(`UPDATE shows SET status = 'completed', judged_at = NOW() WHERE id = ?`, [showId]);

      res.json({
        success: true, message: `Show judged. ${scored.length} entries.`,
        topThree: scored.slice(0, 3).map(e => ({ rank: e.rank, playerId: e.player_id, animalId: e.animal_id, score: e.score })),
      });
    });
  } catch (err: any) {
    if (err.statusCode) return res.status(err.statusCode).json({ error: err.error, message: err.message });
    logger.error('Judge error', err);
    res.status(500).json({ error: 'INTERNAL_ERROR' });
  }
});

// ─── POST /events/shows/:id/claim-reward ─────────────────────────────────────

router.post('/shows/:id/claim-reward', async (req: AuthenticatedRequest, res: Response) => {
  const { id: showId } = req.params;
  const userId = req.user!.id;

  try {
    await withTransaction(async (client) => {
      const [[entryRows]] = await client.execute(`
        SELECT se.*, s.tier, s.name as show_name
        FROM show_entries se JOIN shows s ON s.id = se.show_id
        WHERE se.show_id = ? AND se.player_id = ? AND s.status = 'completed'
        FOR UPDATE
      `, [showId, userId]) as any[];
      const e = (entryRows as any[])[0];
      if (!e) throw { statusCode: 404, error: 'NOT_FOUND' };
      if (e.reward_claimed) throw { statusCode: 400, error: 'ALREADY_CLAIMED' };

      const goldReward = (SHOW_REWARDS[e.tier] || {})[e.rank] || 0;

      if (goldReward > 0) {
        await client.execute('UPDATE users SET gold_balance = gold_balance + ? WHERE id = ?', [goldReward, userId]);
        const [[userRow]] = await client.execute('SELECT gold_balance FROM users WHERE id = ?', [userId]) as any[];
        await client.execute(
          `INSERT INTO transactions (id, user_id, amount, currency, category, balance_after, description, ref_id)
           VALUES (?, ?, ?, 'gold', 'reward', ?, ?, ?)`,
          [uuidv4(), userId, goldReward, (userRow as any[])[0].gold_balance, `${e.show_name} - Rank ${e.rank}`, showId]
        );
      }

      await client.execute('UPDATE show_entries SET reward_claimed = 1 WHERE show_id = ? AND player_id = ?', [showId, userId]);

      res.json({ success: true, rank: e.rank, goldReward, message: goldReward > 0 ? `Claimed ${goldReward} Gold for Rank ${e.rank}!` : `Rank ${e.rank} — no reward for this placement.` });
    });
  } catch (err: any) {
    if (err.statusCode) return res.status(err.statusCode).json({ error: err.error, message: err.message });
    logger.error('Claim reward error', err);
    res.status(500).json({ error: 'INTERNAL_ERROR' });
  }
});

// ─── GET /events/season ───────────────────────────────────────────────────────

router.get('/season', async (req: AuthenticatedRequest, res: Response) => {
  const userId = req.user!.id;
  try {
    const season = await queryOne<any>('SELECT * FROM seasons WHERE is_active = 1 LIMIT 1');
    if (!season) return res.json({ active: false });

    const scoreRow = await queryOne<any>(`
      SELECT COALESCE(SUM(
        CASE se.\`rank\`
          WHEN 1 THEN 100 WHEN 2 THEN 60 WHEN 3 THEN 30
          WHEN 4 THEN 15  WHEN 5 THEN 10 ELSE 5
        END
      ), 0) as season_score
      FROM show_entries se
      JOIN shows s ON s.id = se.show_id
      WHERE se.player_id = ? AND s.season_id = ? AND s.status = 'completed'
    `, [userId, season.id]);

    res.json({
      id: season.id, name: season.name, startDate: season.start_date,
      endDate: season.end_date, isActive: !!season.is_active,
      mySeasonScore: parseInt(scoreRow?.season_score || '0'),
    });
  } catch (err) {
    logger.error('GET /events/season error', err);
    res.status(500).json({ error: 'INTERNAL_ERROR' });
  }
});

export default router;
