require('dotenv').config();
const express = require('express');
const { Pool } = require('pg');
const cors = require('cors');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs'); // Uncomment this after installing bcrypt
const path = require('path');

const app = express();

const pool = new Pool({
  user: process.env.DB_USER,
  host: process.env.DB_HOST,
  database: process.env.DB_NAME,
  password: process.env.DB_PASSWORD,
  port: process.env.DB_PORT,
  ssl: {
    rejectUnauthorized: false // Set to true in production if your DB requires SSL
  }
});

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

const authenticateToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];

  if (token == null) return res.sendStatus(401);

  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) return res.sendStatus(403);
    req.user = user;
    next();
  });
};

app.use(express.static(path.join(__dirname, 'public')));

async function findUserByUsername(username) {
  // Replace the following with a query to your database
  const query = 'SELECT * FROM users WHERE username = $1';
  const { rows } = await pool.query(query, [username]);
  return rows[0]; // Assuming username is unique and only one record will be returned
}

app.post('/login', async (req, res) => {
  const { username, password } = req.body;

  try {
    const user = await findUserByUsername(username);

    if (!user) {
      return res.status(401).json({ message: 'Username or password incorrect' });
    }

    // Compare the hashed password
    const isValid = await bcrypt.compare(password, user.password);

    if (!isValid) {
      return res.status(401).json({ message: 'Username or password incorrect' });
    }

    // User authenticated, create a token
    const accessToken = jwt.sign(
      { userId: user.id, username: user.username },
      process.env.JWT_SECRET,
      { expiresIn: '1h' }
    );

    res.json({ accessToken });
  } catch (error) {
    console.error('Login error:', error);
    res.status(500).json({ message: 'Internal server error' });
  }
});

app.post('/register', async (req, res) => {
  const { username, password } = req.body;

  try {
    // Hash the password
    const hashedPassword = await bcrypt.hash(password, 10);

    // Insert the new user into the database
    const newUser = await pool.query(
      'INSERT INTO users (username, password) VALUES ($1, $2) RETURNING id, username',
      [username, hashedPassword]
    );

    res.json({ userId: newUser.rows[0].id, username: newUser.rows[0].username });
  } catch (err) {
    console.error('Error creating new user', err);
    res.status(500).send('Server error');
  }
});

app.post('/mark-applied', authenticateToken, async (req, res) => {
  const {
    jobId,
    jobLink,
    companyName,
    companyLocation,
    salary,
    jobType,
    jobDescription
  } = req.body;

  const client = await pool.connect();

  try {
    await client.query('BEGIN');

    const insertQueryText = `
      INSERT INTO applied_jobs (
        job_id, 
        job_link, 
        company_name, 
        company_location, 
        salary, 
        job_type, 
        job_description,
        applied_timestamp
      ) 
      VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
      RETURNING *;
    `;

    const insertResult = await client.query(insertQueryText, [
      jobId,
      jobLink,
      companyName,
      companyLocation,
      salary,
      jobType,
      jobDescription
    ]);

    const updateQueryText = `
      UPDATE processed_jobs
      SET applied = TRUE
      WHERE id = $1;
    `;

    await client.query(updateQueryText, [jobId]);

    await client.query('COMMIT');

    res.json({ success: true, message: 'Job marked as applied.', job: insertResult.rows[0] });
  } catch (err) {
    await client.query('ROLLBACK');
    console.error('Database operation failed:', err.stack);
    res.status(500).json({ success: false, message: 'Database operation failed.' });
  } finally {
    client.release();
  }
});

app.get('/job-listings', authenticateToken, async (req, res) => {
  const locationFilter = req.query.locationFilter;

  try {
    let query = `
      SELECT * FROM processed_jobs
      WHERE applied = FALSE
    `;

    // If a location filter is provided and it's not 'ALL'
    if (locationFilter && locationFilter !== 'ALL') {
      // For 'Remote', match 'Remote' in job_location
      if (locationFilter === 'Remote') {
        query += ` AND location_keyword ILIKE '%Remote%'`;
      } else {
        // For state abbreviations, match them in job_location
        query += ` AND location_keyword ILIKE '%${locationFilter}%'`;
      }
    }

    query += ' ORDER BY scrap_time DESC';

    const { rows } = await pool.query(query);
    res.json(rows);
  } catch (err) {
    console.error('Error fetching job listings', err.stack);
    res.status(500).json({ error: 'Internal Server Error' });
  }
});

app.get('/check-applied/:jobId', async (req, res) => {
  const { jobId } = req.params;

  try {
    // Query to check if the job is marked as applied
    const queryText = 'SELECT COUNT(*) FROM applied_jobs WHERE job_id = $1';
    const result = await pool.query(queryText, [jobId]);

    // Check if the count is greater than 0
    const isApplied = parseInt(result.rows[0].count, 10) > 0;

    res.json({ applied: isApplied });
  } catch (err) {
    console.error('Error checking application status:', err);
    res.status(500).json({ success: false, message: 'Database operation failed.' });
  }
});

app.get('/applied-jobs', authenticateToken, async (req, res) => {
  try {
    const query = 'SELECT * FROM applied_jobs ORDER BY applied_timestamp ASC';
    const { rows } = await pool.query(query);
    res.json(rows);
  } catch (err) {
    console.error('Error fetching job listings', err.stack);
    res.status(500).json({ error: 'Internal Server Error' });
  }
});

app.get('/job-listings/:job_jk', authenticateToken, async (req, res) => {
  console.log("Fetching job details for jk ID:", req.params.job_jk);
  try {
    const { job_jk } = req.params;
    const query = 'SELECT * FROM processed_jobs WHERE job_jk = $1';
    const { rows } = await pool.query(query, [job_jk]);
    if (rows.length > 0) {
      res.json(rows[0]);
    } else {
      res.status(404).json({ message: 'Job not found.' });
    }
  } catch (err) {
    console.error('Error fetching job details', err.stack);
    res.status(500).json({ error: 'Internal Server Error' });
  }
});

app.get('/is-applied/:job_jk', authenticateToken, async (req, res) => {
  try {
    const { job_jk } = req.params;
    // Query to check if the job is applied by the user
    const query = `
      SELECT 1 FROM applied_jobs  WHERE job_jk = $1`;
    const { rows } = await pool.query(query, [job_jk]);

    // If rows array is not empty, job is applied
    const isApplied = rows.length > 0;

    res.json({ isApplied });
  } catch (err) {
    console.error('Error in checking applied job', err.stack);
    res.status(500).json({ error: 'Internal Server Error' });
  }
});

app.get('/applied-jobs-count', authenticateToken, async (req, res) => {
  try {
    const query = 'SELECT COUNT(*) FROM applied_jobs';
    const { rows } = await pool.query(query);
    res.json(rows[0]);
  } catch (err) {
    console.error('Error fetching applied jobs count', err.stack);
    res.status(500).json({ error: 'Internal Server Error' });
  }
});

const PORT = 3001;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});