const express = require('express');
const cors = require('cors');
const { Pool } = require('pg');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

const app = express();
const PORT = process.env.PORT || 3001;
const JWT_SECRET = process.env.JWT_SECRET || 'gravl-secret-key-change-in-production';

const pool = new Pool({
  host: process.env.DB_HOST || 'postgres',
  port: process.env.DB_PORT || 5432,
  user: process.env.DB_USER || 'postgres',
  password: process.env.DB_PASSWORD || 'homelab_postgres_2026',
  database: process.env.DB_NAME || 'gravl'
});

app.use(cors());
app.use(express.json());

const authMiddleware = (req, res, next) => {
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) return res.status(401).json({ error: 'No token' });
  try {
    req.user = jwt.verify(token, JWT_SECRET);
    next();
  } catch { res.status(401).json({ error: 'Invalid token' }); }
};

app.get('/api/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

app.post('/api/auth/register', async (req, res) => {
  try {
    const { email, password } = req.body;
    if (!email || !password) return res.status(400).json({ error: 'Email and password required' });
    const hash = await bcrypt.hash(password, 10);
    const result = await pool.query(
      'INSERT INTO users (email, password_hash) VALUES ($1, $2) RETURNING id, email',
      [email.toLowerCase(), hash]
    );
    const token = jwt.sign({ id: result.rows[0].id, email: result.rows[0].email }, JWT_SECRET, { expiresIn: '30d' });
    res.json({ token, user: result.rows[0] });
  } catch (err) {
    if (err.code === '23505') return res.status(400).json({ error: 'Email already exists' });
    console.error('Register error:', err);
    res.status(500).json({ error: 'Server error' });
  }
});

app.post('/api/auth/login', async (req, res) => {
  try {
    const { email, password } = req.body;
    const result = await pool.query('SELECT * FROM users WHERE email = $1', [email.toLowerCase()]);
    if (!result.rows.length) return res.status(401).json({ error: 'Invalid credentials' });
    const user = result.rows[0];
    const valid = await bcrypt.compare(password, user.password_hash);
    if (!valid) return res.status(401).json({ error: 'Invalid credentials' });
    const token = jwt.sign({ id: user.id, email: user.email }, JWT_SECRET, { expiresIn: '30d' });
    const { password_hash, ...safeUser } = user;
    res.json({ token, user: safeUser });
  } catch (err) {
    console.error('Login error:', err);
    res.status(500).json({ error: 'Server error' });
  }
});

app.get('/api/user/profile', authMiddleware, async (req, res) => {
  try {
    const userResult = await pool.query(
      'SELECT id, email, gender, age, height_cm, experience_level, goal, workouts_per_week, onboarding_complete FROM users WHERE id = $1', 
      [req.user.id]
    );
    if (!userResult.rows.length) return res.status(404).json({ error: 'User not found' });
    
    const user = userResult.rows[0];
    
    // Get latest measurements
    const measResult = await pool.query(
      'SELECT weight, neck_cm, waist_cm, hip_cm, body_fat_pct, measured_at FROM user_measurements WHERE user_id = $1 ORDER BY measured_at DESC LIMIT 1',
      [req.user.id]
    );
    
    // Get latest strength
    const strResult = await pool.query(
      'SELECT bench_1rm, squat_1rm, deadlift_1rm, measured_at FROM user_strength WHERE user_id = $1 ORDER BY measured_at DESC LIMIT 1',
      [req.user.id]
    );
    
    res.json({
      ...user,
      measurements: measResult.rows[0] || null,
      strength: strResult.rows[0] || null
    });
  } catch (err) {
    console.error('Profile error:', err);
    res.status(500).json({ error: 'Server error' });
  }
});

app.put('/api/user/profile', authMiddleware, async (req, res) => {
  try {
    const { gender, age, height_cm, experience_level, goal, workouts_per_week, onboarding_complete } = req.body;
    const num = v => (v === '' || v === undefined) ? null : v;
    
    const result = await pool.query(
      `UPDATE users SET gender=$1, age=$2, height_cm=$3, experience_level=$4, goal=$5, workouts_per_week=$6, onboarding_complete=$7 
       WHERE id=$8 RETURNING id, email, gender, age, height_cm, experience_level, goal, workouts_per_week, onboarding_complete`,
      [gender, num(age), num(height_cm), experience_level, goal, num(workouts_per_week), onboarding_complete, req.user.id]
    );
    res.json(result.rows[0]);
  } catch (err) {
    console.error('Update profile error:', err);
    res.status(500).json({ error: 'Server error' });
  }
});

// Add measurements
app.post('/api/user/measurements', authMiddleware, async (req, res) => {
  try {
    const { weight, neck_cm, waist_cm, hip_cm, body_fat_pct } = req.body;
    const num = v => (v === '' || v === undefined) ? null : v;
    
    const result = await pool.query(
      `INSERT INTO user_measurements (user_id, weight, neck_cm, waist_cm, hip_cm, body_fat_pct) 
       VALUES ($1, $2, $3, $4, $5, $6) RETURNING *`,
      [req.user.id, num(weight), num(neck_cm), num(waist_cm), num(hip_cm), num(body_fat_pct)]
    );
    res.json(result.rows[0]);
  } catch (err) {
    console.error('Add measurements error:', err);
    res.status(500).json({ error: 'Server error' });
  }
});

// Get measurements history
app.get('/api/user/measurements', authMiddleware, async (req, res) => {
  try {
    const result = await pool.query(
      'SELECT * FROM user_measurements WHERE user_id = $1 ORDER BY measured_at DESC LIMIT 100',
      [req.user.id]
    );
    res.json(result.rows);
  } catch (err) {
    console.error('Get measurements error:', err);
    res.status(500).json({ error: 'Server error' });
  }
});

// Add strength record
app.post('/api/user/strength', authMiddleware, async (req, res) => {
  try {
    const { bench_1rm, squat_1rm, deadlift_1rm } = req.body;
    const num = v => (v === '' || v === undefined) ? null : v;
    
    const result = await pool.query(
      `INSERT INTO user_strength (user_id, bench_1rm, squat_1rm, deadlift_1rm) 
       VALUES ($1, $2, $3, $4) RETURNING *`,
      [req.user.id, num(bench_1rm), num(squat_1rm), num(deadlift_1rm)]
    );
    res.json(result.rows[0]);
  } catch (err) {
    console.error('Add strength error:', err);
    res.status(500).json({ error: 'Server error' });
  }
});

// Get strength history
app.get('/api/user/strength', authMiddleware, async (req, res) => {
  try {
    const result = await pool.query(
      'SELECT * FROM user_strength WHERE user_id = $1 ORDER BY measured_at DESC LIMIT 100',
      [req.user.id]
    );
    res.json(result.rows);
  } catch (err) {
    console.error('Get strength error:', err);
    res.status(500).json({ error: 'Server error' });
  }
});

// Get all programs
app.get('/api/programs', async (req, res) => {
  try {
    const result = await pool.query('SELECT * FROM programs ORDER BY id');
    res.json(result.rows);
  } catch (err) {
    console.error('Error fetching programs:', err);
    res.status(500).json({ error: 'Database error' });
  }
});

// Get program details with days
app.get('/api/programs/:id', async (req, res) => {
  try {
    const program = await pool.query('SELECT * FROM programs WHERE id = $1', [req.params.id]);
    if (program.rows.length === 0) {
      return res.status(404).json({ error: 'Program not found' });
    }
    
    const days = await pool.query(`
      SELECT pd.*, 
        json_agg(json_build_object(
          'id', pe.id,
          'exercise_id', e.id,
          'name', e.name,
          'muscle_group', e.muscle_group,
          'sets', pe.sets,
          'reps_min', pe.reps_min,
          'reps_max', pe.reps_max,
          'order', pe.order_num
        ) ORDER BY pe.order_num) as exercises
      FROM program_days pd
      LEFT JOIN program_exercises pe ON pd.id = pe.program_day_id
      LEFT JOIN exercises e ON pe.exercise_id = e.id
      WHERE pd.program_id = $1
      GROUP BY pd.id
      ORDER BY pd.day_number
    `, [req.params.id]);
    
    res.json({
      ...program.rows[0],
      days: days.rows
    });
  } catch (err) {
    console.error('Error fetching program:', err);
    res.status(500).json({ error: 'Database error' });
  }
});

// Get exercises for a specific day
app.get('/api/days/:dayId/exercises', async (req, res) => {
  try {
    const result = await pool.query(`
      SELECT pe.id, pe.sets, pe.reps_min, pe.reps_max, pe.order_num,
             e.id as exercise_id, e.name, e.muscle_group, e.description
      FROM program_exercises pe
      JOIN exercises e ON pe.exercise_id = e.id
      WHERE pe.program_day_id = $1
      ORDER BY pe.order_num
    `, [req.params.dayId]);
    res.json(result.rows);
  } catch (err) {
    console.error('Error fetching exercises:', err);
    res.status(500).json({ error: 'Database error' });
  }
});

// Get workout logs for a user and date
app.get('/api/logs', async (req, res) => {
  try {
    const { user_id, date, program_exercise_id } = req.query;
    let query = 'SELECT * FROM workout_logs WHERE 1=1';
    const params = [];
    
    if (user_id) {
      params.push(user_id);
      query += ` AND user_id = $${params.length}`;
    }
    if (date) {
      params.push(date);
      query += ` AND date = $${params.length}`;
    }
    if (program_exercise_id) {
      params.push(program_exercise_id);
      query += ` AND program_exercise_id = $${params.length}`;
    }
    
    query += ' ORDER BY date DESC, set_number ASC';
    
    const result = await pool.query(query, params);
    res.json(result.rows);
  } catch (err) {
    console.error('Error fetching logs:', err);
    res.status(500).json({ error: 'Database error' });
  }
});

// Get last workout for an exercise (for progression)
app.get('/api/logs/last/:programExerciseId', async (req, res) => {
  try {
    const { user_id } = req.query;
    const result = await pool.query(`
      SELECT * FROM workout_logs 
      WHERE program_exercise_id = $1 AND user_id = $2
      ORDER BY date DESC, set_number ASC
      LIMIT 10
    `, [req.params.programExerciseId, user_id || 1]);
    res.json(result.rows);
  } catch (err) {
    console.error('Error fetching last workout:', err);
    res.status(500).json({ error: 'Database error' });
  }
});

// Log a set
app.post('/api/logs', async (req, res) => {
  try {
    const { user_id, program_exercise_id, date, set_number, weight, reps, completed } = req.body;
    
    // Check if log exists for this set
    const existing = await pool.query(
      'SELECT id FROM workout_logs WHERE user_id = $1 AND program_exercise_id = $2 AND date = $3 AND set_number = $4',
      [user_id, program_exercise_id, date, set_number]
    );
    
    let result;
    if (existing.rows.length > 0) {
      // Update existing
      result = await pool.query(
        'UPDATE workout_logs SET weight = $1, reps = $2, completed = $3 WHERE id = $4 RETURNING *',
        [weight, reps, completed, existing.rows[0].id]
      );
    } else {
      // Insert new
      result = await pool.query(
        'INSERT INTO workout_logs (user_id, program_exercise_id, date, set_number, weight, reps, completed) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *',
        [user_id, program_exercise_id, date, set_number, weight, reps, completed]
      );
    }
    
    res.json(result.rows[0]);
  } catch (err) {
    console.error('Error logging set:', err);
    res.status(500).json({ error: 'Database error' });
  }
});

// Calculate suggested weight based on progression
app.get('/api/progression/:programExerciseId', async (req, res) => {
  try {
    const { user_id } = req.query;
    
    // Get exercise details
    const exerciseInfo = await pool.query(`
      SELECT pe.*, e.name FROM program_exercises pe
      JOIN exercises e ON pe.exercise_id = e.id
      WHERE pe.id = $1
    `, [req.params.programExerciseId]);
    
    if (exerciseInfo.rows.length === 0) {
      return res.status(404).json({ error: 'Exercise not found' });
    }
    
    const exercise = exerciseInfo.rows[0];
    
    // Get last workout logs for this exercise
    const lastLogs = await pool.query(`
      SELECT * FROM workout_logs 
      WHERE program_exercise_id = $1 AND user_id = $2 AND completed = true
      ORDER BY date DESC, set_number ASC
      LIMIT $3
    `, [req.params.programExerciseId, user_id || 1, exercise.sets]);
    
    if (lastLogs.rows.length === 0) {
      return res.json({
        suggestedWeight: 20, // Starting weight
        reason: 'No previous data - start light'
      });
    }
    
    const lastWeight = lastLogs.rows[0].weight;
    const allSetsHitMaxReps = lastLogs.rows.every(log => log.reps >= exercise.reps_max);
    
    if (allSetsHitMaxReps) {
      // Progress: increase weight by 2.5kg
      return res.json({
        suggestedWeight: lastWeight + 2.5,
        reason: `Hit ${exercise.reps_max} reps on all sets - increase weight!`
      });
    }
    
    return res.json({
      suggestedWeight: lastWeight,
      reason: 'Keep same weight until you hit max reps on all sets'
    });
  } catch (err) {
    console.error('Error calculating progression:', err);
    res.status(500).json({ error: 'Database error' });
  }
});

// Get today's workout based on program day cycle
app.get('/api/today/:programId', async (req, res) => {
  try {
    const { week } = req.query;
    const currentWeek = week || 1;
    
    // Get program days
    const days = await pool.query(`
      SELECT pd.*, 
        json_agg(json_build_object(
          'id', pe.id,
          'exercise_id', e.id,
          'name', e.name,
          'muscle_group', e.muscle_group,
          'sets', pe.sets,
          'reps_min', pe.reps_min,
          'reps_max', pe.reps_max,
          'order', pe.order_num
        ) ORDER BY pe.order_num) as exercises
      FROM program_days pd
      LEFT JOIN program_exercises pe ON pd.id = pe.program_day_id
      LEFT JOIN exercises e ON pe.exercise_id = e.id
      WHERE pd.program_id = $1
      GROUP BY pd.id
      ORDER BY pd.day_number
    `, [req.params.programId]);
    
    res.json({
      week: parseInt(currentWeek),
      days: days.rows
    });
  } catch (err) {
    console.error('Error fetching today workout:', err);
    res.status(500).json({ error: 'Database error' });
  }
});

app.listen(PORT, () => {
  console.log(`Gravl API running on port ${PORT}`);
});
