import { Router, Request, Response } from 'express';
import bcrypt from 'bcryptjs';
import { v4 as uuidv4 } from 'uuid';
import crypto from 'crypto';
import { z } from 'zod';
import { queryOne, queryMany, exec, withTransaction } from '../db/pool';
import { generateAccessToken, generateRefreshToken, verifyRefreshToken } from '../middleware/auth';
import { logger } from '../lib/logger';
import { AnimalSex } from '@ranchlands/shared';
import { generateStarterGenotype, genotypeToPhenotype, computeRarity } from '../lib/genetics';

const router = Router();

const registerSchema = z.object({
  email: z.string().email(),
  username: z.string().min(3).max(24).regex(/^[a-zA-Z0-9_-]+$/),
  password: z.string().min(8).max(128),
  ranchName: z.string().min(3).max(100),
});

const loginSchema = z.object({
  email: z.string().email(),
  password: z.string(),
});

// ─── POST /auth/register ──────────────────────────────────────────────────────

router.post('/register', async (req: Request, res: Response) => {
  const parsed = registerSchema.safeParse(req.body);
  if (!parsed.success) {
    return res.status(400).json({ error: 'VALIDATION_ERROR', message: 'Invalid input', details: parsed.error.flatten().fieldErrors });
  }

  const { email, username, password, ranchName } = parsed.data;

  try {
    await withTransaction(async (client) => {
      const [existing] = await client.execute(
        'SELECT id FROM users WHERE email = ? OR username = ? LIMIT 1',
        [email.toLowerCase(), username]
      ) as any[];

      if ((existing as any[]).length > 0) {
        throw { statusCode: 409, error: 'CONFLICT', message: 'Email or username already taken' };
      }

      const passwordHash = await bcrypt.hash(password, 12);
      const userId   = uuidv4();
      const ranchId  = uuidv4();
      const STARTER_GOLD = 500;
      const restrictedUntil = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);

      await client.execute(
        `INSERT INTO users (id, email, password_hash, username, gold_balance, market_restricted_until)
         VALUES (?, ?, ?, ?, ?, ?)`,
        [userId, email.toLowerCase(), passwordHash, username, STARTER_GOLD, restrictedUntil]
      );

      await client.execute(
        `INSERT INTO ranches (id, owner_id, name, plot_count, max_plot_count) VALUES (?, ?, ?, 4, 20)`,
        [ranchId, userId, ranchName]
      );

      await client.execute(
        `INSERT INTO transactions (id, user_id, amount, currency, category, balance_after, description)
         VALUES (?, ?, ?, 'gold', 'starter', ?, 'Welcome to Ranchlands!')`,
        [uuidv4(), userId, STARTER_GOLD, STARTER_GOLD]
      );

      // Create 3 starter animals
      const starters = [
        { name: 'Buck',   breed: 'Angus',   sex: AnimalSex.MALE   },
        { name: 'Bessie', breed: 'Angus',   sex: AnimalSex.FEMALE },
        { name: 'Millie', breed: 'Holstein',sex: AnimalSex.FEMALE },
      ];

      for (const s of starters) {
        const animalId = uuidv4();
        const genotype = generateStarterGenotype();
        const phenotype = genotypeToPhenotype(genotype);
        const rarity = computeRarity(genotype);
        const bornAt = new Date(Date.now() - 200 * 24 * 60 * 60 * 1000);

        await client.execute(
          `INSERT INTO animals (id, owner_id, ranch_id, name, breed, sex, born_at, stage, rarity_tier)
           VALUES (?, ?, ?, ?, ?, ?, ?, 'adult', ?)`,
          [animalId, userId, ranchId, s.name, s.breed, s.sex, bornAt, rarity]
        );

        await client.execute(
          `INSERT INTO animal_phenotypes
             (animal_id, weight_score, milk_yield_score, growth_rate_score,
              temperament_score, coat_quality_score, fertility_score, hardiness_score, show_potential_score)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
          [animalId,
           phenotype.weightScore, phenotype.milkYieldScore, phenotype.growthRateScore,
           phenotype.temperamentScore, phenotype.coatQualityScore, phenotype.fertilityScore,
           phenotype.hardinessScore, phenotype.showPotentialScore]
        );

        await client.execute(
          `INSERT INTO animal_genotypes
             (animal_id, g_weight, g_milk_yield, g_growth_rate, g_temperament,
              g_coat_quality, g_fertility, g_hardiness, g_show_potential)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
          [animalId,
           genotype.gWeight, genotype.gMilkYield, genotype.gGrowthRate, genotype.gTemperament,
           genotype.gCoatQuality, genotype.gFertility, genotype.gHardiness, genotype.gShowPotential]
        );
      }

      const accessToken  = generateAccessToken({ sub: userId, username, email: email.toLowerCase(), status: 'active' });
      const refreshToken = generateRefreshToken(userId);
      const tokenHash    = crypto.createHash('sha256').update(refreshToken).digest('hex');

      await client.execute(
        `INSERT INTO refresh_tokens (id, user_id, token_hash, expires_at)
         VALUES (?, ?, ?, DATE_ADD(NOW(), INTERVAL 30 DAY))`,
        [uuidv4(), userId, tokenHash]
      );

      logger.info(`Registered: ${username} (${userId})`);

      res.status(201).json({
        token: accessToken,
        refreshToken,
        user: {
          id: userId, email: email.toLowerCase(), username,
          status: 'active', goldBalance: STARTER_GOLD, premiumBalance: 0,
          prestigePoints: 0, marketRestrictedUntil: restrictedUntil, createdAt: new Date(),
        },
      });
    });
  } catch (err: any) {
    if (err.statusCode) return res.status(err.statusCode).json({ error: err.error, message: err.message });
    logger.error('Register error', err);
    res.status(500).json({ error: 'INTERNAL_ERROR', message: 'Registration failed' });
  }
});

// ─── POST /auth/login ─────────────────────────────────────────────────────────

router.post('/login', async (req: Request, res: Response) => {
  const parsed = loginSchema.safeParse(req.body);
  if (!parsed.success) return res.status(400).json({ error: 'VALIDATION_ERROR', message: 'Invalid input' });

  const { email, password } = parsed.data;

  try {
    const user = await queryOne<any>('SELECT * FROM users WHERE email = ?', [email.toLowerCase()]);
    if (!user) return res.status(401).json({ error: 'INVALID_CREDENTIALS', message: 'Invalid email or password' });
    if (user.status === 'banned') return res.status(403).json({ error: 'BANNED', message: 'This account has been banned.' });
    if (!user.password_hash) return res.status(401).json({ error: 'OAUTH_ACCOUNT', message: 'Please use OAuth to login' });

    const valid = await bcrypt.compare(password, user.password_hash);
    if (!valid) return res.status(401).json({ error: 'INVALID_CREDENTIALS', message: 'Invalid email or password' });

    await exec('UPDATE users SET last_login_at = NOW() WHERE id = ?', [user.id]);

    const accessToken  = generateAccessToken({ sub: user.id, username: user.username, email: user.email, status: user.status });
    const refreshToken = generateRefreshToken(user.id);
    const tokenHash    = crypto.createHash('sha256').update(refreshToken).digest('hex');

    await exec(
      `INSERT INTO refresh_tokens (id, user_id, token_hash, expires_at)
       VALUES (?, ?, ?, DATE_ADD(NOW(), INTERVAL 30 DAY))
       ON DUPLICATE KEY UPDATE token_hash = token_hash`,
      [uuidv4(), user.id, tokenHash]
    );

    const ranch = await queryOne<any>('SELECT * FROM ranches WHERE owner_id = ?', [user.id]);

    res.json({
      token: accessToken,
      refreshToken,
      user: {
        id: user.id, email: user.email, username: user.username, status: user.status,
        goldBalance: parseInt(user.gold_balance),
        premiumBalance: parseInt(user.premium_balance),
        prestigePoints: parseInt(user.prestige_points),
        marketRestrictedUntil: user.market_restricted_until,
        createdAt: user.created_at, lastLoginAt: user.last_login_at,
        ranch: ranch ? { id: ranch.id, name: ranch.name, plotCount: ranch.plot_count, prestigeLevel: ranch.prestige_level } : null,
      },
    });
  } catch (err) {
    logger.error('Login error', err);
    res.status(500).json({ error: 'INTERNAL_ERROR', message: 'Login failed' });
  }
});

// ─── POST /auth/refresh ───────────────────────────────────────────────────────

router.post('/refresh', async (req: Request, res: Response) => {
  const { refreshToken } = req.body;
  if (!refreshToken) return res.status(400).json({ error: 'MISSING_TOKEN', message: 'Refresh token required' });

  try {
    const payload   = verifyRefreshToken(refreshToken);
    const tokenHash = crypto.createHash('sha256').update(refreshToken).digest('hex');
    const stored    = await queryOne<any>(
      'SELECT * FROM refresh_tokens WHERE token_hash = ? AND expires_at > NOW()',
      [tokenHash]
    );

    if (!stored) return res.status(401).json({ error: 'INVALID_TOKEN', message: 'Refresh token invalid or expired' });

    const user = await queryOne<any>('SELECT * FROM users WHERE id = ?', [payload.sub]);
    if (!user || user.status === 'banned') return res.status(401).json({ error: 'UNAUTHORIZED' });

    const accessToken = generateAccessToken({ sub: user.id, username: user.username, email: user.email, status: user.status });
    res.json({ token: accessToken });
  } catch {
    res.status(401).json({ error: 'INVALID_TOKEN', message: 'Invalid refresh token' });
  }
});

// ─── POST /auth/logout ────────────────────────────────────────────────────────

router.post('/logout', async (req: Request, res: Response) => {
  const { refreshToken } = req.body;
  if (refreshToken) {
    const tokenHash = crypto.createHash('sha256').update(refreshToken).digest('hex');
    await exec('DELETE FROM refresh_tokens WHERE token_hash = ?', [tokenHash]);
  }
  res.json({ success: true });
});

export default router;
