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

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

const LISTING_FEE  = 0.05;
const SALE_TAX     = 0.03;
const MAX_LISTINGS = 50;
const ANTI_SNIPE_MS = 5 * 60 * 1000;

function mapListing(row: any) {
  return {
    id: row.id, sellerId: row.seller_id, sellerUsername: row.seller_username,
    listingType: row.listing_type, price: parseInt(row.price),
    buyoutPrice: row.buyout_price ? parseInt(row.buyout_price) : null,
    currentBid: row.current_bid ? parseInt(row.current_bid) : null,
    currentBidderId: row.current_bidder_id,
    expiresAt: row.expires_at, status: row.status, createdAt: row.created_at,
    animal: row.animal_id ? {
      id: row.animal_id, name: row.animal_name, breed: row.breed,
      sex: row.sex, stage: row.stage, rarityTier: row.rarity_tier,
      status: row.animal_status,
      health: row.health ? parseFloat(row.health) : null,
      condition: row.condition ? parseFloat(row.condition) : null,
      phenotype: row.weight_score != null ? {
        weightScore: parseFloat(row.weight_score),
        milkYieldScore: parseFloat(row.milk_yield_score),
        growthRateScore: parseFloat(row.growth_rate_score),
        showPotentialScore: parseFloat(row.show_potential_score),
      } : null,
    } : null,
  };
}

const BASE_SELECT = `
  SELECT ml.*, u.username as seller_username,
    a.name as animal_name, a.breed, a.sex, a.stage, a.rarity_tier,
    a.status as animal_status, a.health, a.\`condition\`,
    ap.weight_score, ap.milk_yield_score, ap.growth_rate_score, ap.show_potential_score
  FROM market_listings ml
  LEFT JOIN animals a          ON a.id  = ml.animal_id
  LEFT JOIN animal_phenotypes ap ON ap.animal_id = a.id
  LEFT JOIN users u            ON u.id  = ml.seller_id
`;

// ─── GET /market/listings ─────────────────────────────────────────────────────

router.get('/listings', async (req: AuthenticatedRequest, res: Response) => {
  const { breed, rarity, listingType, sort = 'newest', page = '1', pageSize = '20' } = req.query;
  const offset = (parseInt(page as string) - 1) * parseInt(pageSize as string);

  const conditions = [`ml.status = 'active'`, `ml.expires_at > NOW()`];
  const params: unknown[] = [];

  if (breed)       { conditions.push('a.breed = ?');        params.push(breed); }
  if (rarity)      { conditions.push('a.rarity_tier = ?');  params.push(rarity); }
  if (listingType) { conditions.push('ml.listing_type = ?');params.push(listingType); }

  const sortMap: Record<string, string> = {
    newest: 'ml.created_at DESC', price_asc: 'ml.price ASC',
    price_desc: 'ml.price DESC', ending_soon: 'ml.expires_at ASC',
  };
  const orderBy = sortMap[sort as string] || 'ml.created_at DESC';
  const where = conditions.join(' AND ');

  try {
    const countRow = await queryOne<any>(`SELECT COUNT(*) as cnt FROM market_listings ml LEFT JOIN animals a ON a.id = ml.animal_id WHERE ${where}`, params);
    const total = parseInt(countRow?.cnt || '0');

    const rows = await queryMany<any>(`${BASE_SELECT} WHERE ${where} ORDER BY ${orderBy} LIMIT ? OFFSET ?`, [...params, parseInt(pageSize as string), offset]);

    res.json({ data: rows.map(mapListing), total, page: parseInt(page as string), pageSize: parseInt(pageSize as string), hasMore: offset + rows.length < total });
  } catch (err) {
    logger.error('GET /market/listings error', err);
    res.status(500).json({ error: 'INTERNAL_ERROR' });
  }
});

// ─── POST /market/listings ────────────────────────────────────────────────────

router.post('/listings', async (req: AuthenticatedRequest, res: Response) => {
  const parsed = z.object({
    animalId: z.string().uuid(),
    listingType: z.nativeEnum(ListingType),
    price: z.number().int().positive(),
    buyoutPrice: z.number().int().positive().optional(),
    durationDays: z.number().int().min(1).max(7).default(7),
  }).safeParse(req.body);

  if (!parsed.success) return res.status(400).json({ error: 'VALIDATION_ERROR' });
  const { animalId, listingType, price, buyoutPrice, durationDays } = parsed.data;
  const userId = req.user!.id;

  try {
    const user = await queryOne<any>('SELECT * FROM users WHERE id = ?', [userId]);
    if (user?.market_restricted_until && new Date(user.market_restricted_until) > new Date()) {
      return res.status(403).json({ error: 'MARKET_RESTRICTED', message: 'New accounts cannot list for 7 days.', restrictedUntil: user.market_restricted_until });
    }

    const countRow = await queryOne<any>(`SELECT COUNT(*) as cnt FROM market_listings WHERE seller_id = ? AND status = 'active'`, [userId]);
    if (parseInt(countRow?.cnt || '0') >= MAX_LISTINGS) return res.status(400).json({ error: 'TOO_MANY_LISTINGS' });

    await withTransaction(async (client) => {
      const [[animalRows]] = await client.execute('SELECT * FROM animals WHERE id = ? AND owner_id = ? AND died_at IS NULL FOR UPDATE', [animalId, userId]) as any[];
      const a = (animalRows as any[])[0];
      if (!a) throw { statusCode: 404, error: 'NOT_FOUND' };
      if (a.status === 'pregnant') throw { statusCode: 400, error: 'INVALID', message: 'Cannot list a pregnant animal' };

      const [[existRows]] = await client.execute(`SELECT id FROM market_listings WHERE animal_id = ? AND status = 'active'`, [animalId]) as any[];
      if ((existRows as any[]).length) throw { statusCode: 409, error: 'ALREADY_LISTED' };

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

      const expiresAt = new Date(Date.now() + durationDays * 24 * 60 * 60 * 1000);
      const listingId = uuidv4();

      await client.execute(
        `INSERT INTO market_listings (id, seller_id, animal_id, listing_type, price, buyout_price, expires_at)
         VALUES (?, ?, ?, ?, ?, ?, ?)`,
        [listingId, userId, animalId, listingType, price, buyoutPrice || null, expiresAt]
      );

      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)
         VALUES (?, ?, ?, 'gold', 'fee', ?, ?)`,
        [uuidv4(), userId, -fee, (userRow as any[])[0].gold_balance, `Listing fee: ${a.name}`]
      );

      res.status(201).json({ listingId, listingFee: fee, message: `${a.name} listed for ${price} Gold` });
    });
  } catch (err: any) {
    if (err.statusCode) return res.status(err.statusCode).json({ error: err.error, message: err.message });
    logger.error('POST /market/listings error', err);
    res.status(500).json({ error: 'INTERNAL_ERROR' });
  }
});

// ─── POST /market/listings/:id/buy ───────────────────────────────────────────

router.post('/listings/:id/buy', async (req: AuthenticatedRequest, res: Response) => {
  const { id } = req.params;
  const buyerId = req.user!.id;

  try {
    await withTransaction(async (client) => {
      const [[lRows]] = await client.execute(
        `SELECT * FROM market_listings WHERE id = ? AND status = 'active' AND expires_at > NOW() FOR UPDATE`,
        [id]
      ) as any[];
      const l = (lRows as any[])[0];
      if (!l) throw { statusCode: 404, error: 'NOT_FOUND' };
      if (l.seller_id === buyerId) throw { statusCode: 400, error: 'INVALID', message: 'Cannot buy your own listing' };

      const salePrice = l.listing_type === 'auction' && l.buyout_price ? parseInt(l.buyout_price) : parseInt(l.price);
      const tax = Math.ceil(salePrice * SALE_TAX);
      const proceeds = salePrice - tax;

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

      if (l.current_bidder_id && l.current_bid) {
        await client.execute('UPDATE users SET gold_balance = gold_balance + ? WHERE id = ?', [parseInt(l.current_bid), l.current_bidder_id]);
      }

      await client.execute('UPDATE users SET gold_balance = gold_balance + ? WHERE id = ?', [proceeds, l.seller_id]);
      await client.execute('UPDATE animals SET owner_id = ?, updated_at = NOW() WHERE id = ?', [buyerId, l.animal_id]);
      await client.execute(`UPDATE market_listings SET status = 'sold', updated_at = NOW() WHERE id = ?`, [id]);

      const [[buyerRow]] = await client.execute('SELECT gold_balance FROM users WHERE id = ?', [buyerId]) as any[];
      const [[sellerRow]] = await client.execute('SELECT gold_balance FROM users WHERE id = ?', [l.seller_id]) as any[];

      await client.execute(
        `INSERT INTO transactions (id, user_id, amount, currency, category, balance_after, description, ref_id) VALUES
         (?, ?, ?, 'gold', 'market_buy',  ?, 'Market purchase', ?),
         (?, ?, ?, 'gold', 'market_sale', ?, 'Market sale',     ?)`,
        [uuidv4(), buyerId, -salePrice, (buyerRow as any[])[0].gold_balance, id,
         uuidv4(), l.seller_id, proceeds, (sellerRow as any[])[0].gold_balance, id]
      );

      res.json({ success: true, message: 'Purchase successful!' });
    });
  } catch (err: any) {
    if (err.statusCode) return res.status(err.statusCode).json({ error: err.error, message: err.message });
    logger.error('Buy error', err);
    res.status(500).json({ error: 'INTERNAL_ERROR' });
  }
});

// ─── POST /market/listings/:id/bid ───────────────────────────────────────────

router.post('/listings/:id/bid', async (req: AuthenticatedRequest, res: Response) => {
  const { id } = req.params;
  const parsed = z.object({ amount: z.number().int().positive() }).safeParse(req.body);
  if (!parsed.success) return res.status(400).json({ error: 'VALIDATION_ERROR' });

  const bidderId = req.user!.id;
  const { amount } = parsed.data;

  try {
    await withTransaction(async (client) => {
      const [[lRows]] = await client.execute(
        `SELECT * FROM market_listings WHERE id = ? AND listing_type = 'auction' AND status = 'active' AND expires_at > NOW() FOR UPDATE`,
        [id]
      ) as any[];
      const l = (lRows as any[])[0];
      if (!l) throw { statusCode: 404, error: 'NOT_FOUND' };
      if (l.seller_id === bidderId) throw { statusCode: 400, error: 'INVALID', message: 'Cannot bid on your own auction' };
      if (l.current_bidder_id === bidderId) throw { statusCode: 400, error: 'INVALID', message: 'You are already highest bidder' };

      const minBid = l.current_bid ? parseInt(l.current_bid) + 1 : parseInt(l.price);
      if (amount < minBid) throw { statusCode: 400, error: 'BID_TOO_LOW', message: `Minimum bid: ${minBid} Gold` };

      const [deduct] = await client.execute(
        'UPDATE users SET gold_balance = gold_balance - ? WHERE id = ? AND gold_balance >= ?',
        [amount, bidderId, amount]
      ) as any[];
      if ((deduct as any).affectedRows === 0) throw { statusCode: 400, error: 'INSUFFICIENT_GOLD' };

      if (l.current_bidder_id && l.current_bid) {
        await client.execute('UPDATE users SET gold_balance = gold_balance + ? WHERE id = ?', [parseInt(l.current_bid), l.current_bidder_id]);
      }

      const timeLeft = new Date(l.expires_at).getTime() - Date.now();
      const newExpiry = timeLeft < ANTI_SNIPE_MS ? new Date(Date.now() + ANTI_SNIPE_MS) : new Date(l.expires_at);

      await client.execute(
        `UPDATE market_listings SET current_bid = ?, current_bidder_id = ?, expires_at = ?, updated_at = NOW() WHERE id = ?`,
        [amount, bidderId, newExpiry, id]
      );

      res.json({ success: true, newBid: amount, expiresAt: newExpiry });
    });
  } catch (err: any) {
    if (err.statusCode) return res.status(err.statusCode).json({ error: err.error, message: err.message });
    logger.error('Bid error', err);
    res.status(500).json({ error: 'INTERNAL_ERROR' });
  }
});

// ─── DELETE /market/listings/:id ─────────────────────────────────────────────

router.delete('/listings/:id', async (req: AuthenticatedRequest, res: Response) => {
  const { id } = req.params;
  const userId  = req.user!.id;

  try {
    await withTransaction(async (client) => {
      const [[lRows]] = await client.execute(
        `SELECT * FROM market_listings WHERE id = ? AND seller_id = ? AND status = 'active' FOR UPDATE`,
        [id, userId]
      ) as any[];
      const l = (lRows as any[])[0];
      if (!l) throw { statusCode: 404, error: 'NOT_FOUND' };
      if (l.listing_type === 'auction' && l.current_bid) throw { statusCode: 400, error: 'HAS_BIDS', message: 'Cannot cancel auction with bids' };
      await client.execute(`UPDATE market_listings SET status = 'cancelled', updated_at = NOW() WHERE id = ?`, [id]);
      res.json({ success: true });
    });
  } catch (err: any) {
    if (err.statusCode) return res.status(err.statusCode).json({ error: err.error, message: err.message });
    logger.error('Cancel listing error', err);
    res.status(500).json({ error: 'INTERNAL_ERROR' });
  }
});

// ─── GET /market/my-listings ──────────────────────────────────────────────────

router.get('/my-listings', async (req: AuthenticatedRequest, res: Response) => {
  const userId = req.user!.id;
  try {
    const rows = await queryMany<any>(`${BASE_SELECT} WHERE ml.seller_id = ? AND ml.status = 'active' ORDER BY ml.created_at DESC`, [userId]);
    res.json(rows.map(mapListing));
  } catch (err) {
    logger.error('GET /market/my-listings error', err);
    res.status(500).json({ error: 'INTERNAL_ERROR' });
  }
});

export default router;
