import badWords from 'badwords-list';
import query from '../server-utils/query.js';
import { declareHandler } from '../server-utils/routesHandler.js';

const landingUri = process.env.LANDING_URI || 'http://localhost:3000';
const shortUrlPrefix = process.env.URL_SHORTENER_PREFIX || 'http://localhost:3001/s';
const chars = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ23456789';

// 65 ^ 6 = 75,418,890,625 possible short URLs
// Problem: rude words are technically possible.
const generateUrlUUID = () => {
  let result = '';
  for (let i = 0; i < 6; i++) {
    result += chars[Math.floor(Math.random() * chars.length)];
  }
  return result;
};

export const generateSafeUrlUUID = () => {
  let attempts = 0;
  while (attempts++ < 1000) {
    const id = generateUrlUUID();
    if (!badWords.array.some((word) => id.includes(word))) {
      return id;
    }
  }
};

interface ShortUrlOptions {
  userId?: number;
  shortcode?: string;
  isManual?: boolean;
}
export const shortenUrl = async (longUrl: string, options?: ShortUrlOptions) => {
  let attempts = 0;
  let shortCode;

  const { userId, shortcode: shortcodeOverride, isManual } = options || {};

  if (shortcodeOverride) {
    // Ensure the short code doesn't already exist
    const result = await query(
      `
      SELECT short_code FROM short_urls
      WHERE short_code = $1
      `,
      [shortcodeOverride]
    );
    if (result.rows.length) {
      throw new Error('Short code already exists');
    }
  } else if (!isManual) {
    // If it's not a manual, check if the longUrl already exists in the database
    const result = await query(
      `
        SELECT short_code FROM short_urls
        WHERE long_url = $1 AND user_id = $2
      `,
      [longUrl, userId]
    );
    if (result.rows.length) {
      shortCode = result.rows[0].short_code;
      if (shortCode) {
        return shortUrlPrefix + '/' + shortCode;
      }
    }
  }

  let verifiedUserId: number | null = null;
  // Check if user exists
  if (userId) {
    const user = await query(`SELECT id FROM users WHERE id = $1`, [userId]);
    if (user.rows.length > 0) {
      verifiedUserId = userId;
    }
  }

  if (shortcodeOverride) {
    shortCode = shortcodeOverride;
  } else {
    shortCode = generateSafeUrlUUID();
  }

  while (attempts++ < 1000) {
    // Save shortUrl to database
    const newRow = await query(
      `
        INSERT INTO short_urls (short_code, long_url, user_id, is_manual)
        VALUES ($1, $2, $3, $4)
        ON CONFLICT (short_code) DO NOTHING
        RETURNING id
      `,
      [shortCode, longUrl, verifiedUserId, !!isManual]
    );
    if (newRow.rows.length) {
      break;
    } else if (shortcodeOverride) {
      throw new Error('Short code already exists');
    }

    shortCode = generateSafeUrlUUID();
  }

  return shortUrlPrefix + '/' + shortCode;
};

export const generateShortUrl = declareHandler({
  func: async (req, res) => {
    const { user, body } = req;
    const { url, shortcode } = body;
    const userId = user?.id;

    try {
      const shortUrl = await shortenUrl(url, {
        userId,
        shortcode,
        isManual: true,
      });
      res.send({ shortUrl });
    } catch (e) {
      res.status(400).send({ error: e.message });
      return;
    }
  },
});

export const getLongUrl = declareHandler({
  func: async (req, res) => {
    const ip = req.connection.remoteAddress || req.headers['x-forwarded-for'] || req.headers['x-real-ip'];
    const { browser, version, os, platform, source, isMobile } = req.useragent;
    const requestInformation = {
      ip,
      useragent: {
        browser,
        version,
        os,
        platform,
        source,
        isMobile,
      },
      headers: req.headers,
    };

    const { id } = req.params;
    const longUrl = await query(`SELECT id, long_url FROM short_urls WHERE short_code = $1`, [id]);

    if (longUrl.rows.length > 0) {
      // Save click to database
      await query(`INSERT INTO short_url_clicks (short_url_id, request_information) VALUES ($1, $2) `, [
        longUrl.rows[0].id,
        JSON.stringify(requestInformation),
      ]);
      // Redirect to longUrl
      res.redirect(301, longUrl.rows[0].long_url);
    } else {
      // Redirect to homepage with error if shortUrl not found
      res.redirect(301, landingUri + '?error=shorturl');
    }
  },
});

export const getUserShortUrls = declareHandler({
  func: async (req, res) => {
    const { user } = req;
    const shortUrls = await query(
      `
      SELECT su.short_code, su.long_url, COUNT(suc.short_url_id) AS click_count, su.created_at
      FROM short_urls su
      LEFT JOIN short_url_clicks suc
      ON su.id = suc.short_url_id AND suc.created_at >= NOW() - INTERVAL '30 days'
      WHERE su.user_id = $1
      GROUP BY su.short_code, su.long_url, su.created_at
      ORDER BY su.created_at DESC;
    `,
      [user.id]
    );
    res.send(
      shortUrls.rows.map((row) => ({
        short_url: `${shortUrlPrefix}/${row.short_code}`,
        short_code: row.short_code,
        long_url: row.long_url,
        clicks: row.click_count,
        created_at: row.created_at,
      }))
    );
  },
});

export const getShortUrlWithStats = declareHandler({
  func: async (req, res) => {
    const { shortCode } = req.params;
    const shortUrl = await query(
      `
      SELECT short_code, long_url, created_at, id
      FROM short_urls WHERE short_code = $1
    `,
      [shortCode]
    );

    if (!shortUrl.rows.length) {
      res.status(404).send({ error: 'Short URL not found' });
      return;
    }

    const clicks = await query(
      `
      SELECT created_at, request_information
      FROM short_url_clicks
      WHERE short_url_id = $1
      ORDER BY created_at DESC
      LIMIT 100
    `,
      [shortUrl.rows[0].id]
    );

    const clickCount30Days = await query(
      `
      SELECT COUNT(*) FROM short_url_clicks
      WHERE short_url_id = $1
      AND created_at > NOW() - INTERVAL '30 days'
    `,
      [shortUrl.rows[0].id]
    );

    const clickCountLifetime = await query(
      `
      SELECT COUNT(*) FROM short_url_clicks
      WHERE short_url_id = $1
    `,
      [shortUrl.rows[0].id]
    );

    res.send({
      error: null,
      shortUrl: `${shortUrlPrefix}/${shortUrl.rows[0].short_code}`,
      longUrl: shortUrl.rows[0].long_url,
      shortCode: shortUrl.rows[0].short_code,
      createdAt: shortUrl.rows[0].created_at,
      clicks: clicks.rows,
      clickCount30Days: clickCount30Days.rows[0].count,
      clickCountLifetime: clickCountLifetime.rows[0].count,
    });
  },
});
