import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
import multer from 'multer';
import generateBandEnergy from '../generators/generateBandEnergy.js';
import generateTrackId from '../generators/generateTrackId.js';
import query from '../server-utils/query.js';
import { declareHandler } from '../server-utils/routesHandler.js';
import { shortenUrl } from './urlShortener.js';

/**
 * This file contains route handlers for sharing the project exports.
 *
 * We allow users to export their projects and share them with other users via a
 * wavtool link. This is done by uploading the exported clip to S3 and storing
 * the metadata in the database.
 */

const s3 = new S3Client({ region: 'us-east-1' });

const landingUri = process.env.LANDING_URI || 'http://localhost:3000';
const serverUri = process.env.SERVER_URI;

// This is because the track sharing page is handled by our server nextjs installation,
// but in any of the deployment environments, we have the redirects in place route the requests from wavtool.com/tracks to the api.wavtool.com/tracks route.
const trackPrefix = process.env.NODE_ENV === 'development' ? serverUri : landingUri;

const upload: any = multer({ limits: { fieldSize: Math.pow(10, 8) } }); // 100mb limit

interface HandleFileSharingUploadResponse {
  success: boolean;
  bytes: number;
  name: string;
  url: string;
}

export const handleFileSharingUpload = declareHandler({
  middleware: upload.single('file'),
  func: async (req, res) => {
    const { user } = req;

    const trackName = req.body.name;
    const projectUuid = req.body.projectUuid;
    const trackAuthorId = req.user.id;
    const buffer = req.file.buffer;
    const trackUuid = generateTrackId();

    const s3Key = `${trackAuthorId}/${trackUuid}.mp3`;
    await s3.send(
      new PutObjectCommand({
        Bucket: 'uploaded-tracks',
        Key: s3Key,
        Body: buffer,
      })
    );

    const trackUrl = `https://uploaded-tracks.s3.amazonaws.com/${s3Key}`;

    const bandEnergy = await generateBandEnergy(buffer);

    let projectId = null;
    if (projectUuid) {
      // Get latest project from uuid
      const project = await query(
        `
          SELECT id
          FROM projects
          WHERE uuid = $1 AND superseded_by IS NULL
          ORDER BY updated_at DESC
          LIMIT 1
        `,
        [projectUuid]
      );
      projectId = project.rows[0].id;
    }

    if (projectId) {
      await query(
        `INSERT INTO tracks_for_sharing (s3_key, author_id, name, uuid, s3_url, band_energy, project_id) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id`,
        [s3Key, trackAuthorId, trackName, trackUuid, trackUrl, JSON.stringify(bandEnergy), projectId]
      );
    } else {
      await query(
        `INSERT INTO tracks_for_sharing (s3_key, author_id, name, uuid, s3_url, band_energy) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id`,
        [s3Key, trackAuthorId, trackName, trackUuid, trackUrl, JSON.stringify(bandEnergy)]
      );
    }

    const url = trackPrefix + '/tracks/' + trackUuid;
    const shortUrl = await shortenUrl(url, { userId: user.id });

    const response: HandleFileSharingUploadResponse = {
      success: true,
      bytes: req.file.size,
      name: trackName,
      url: shortUrl,
    };
    res.send(response);
  },
});

export const getSharedTracks = declareHandler({
  func: async (req, res) => {
    const { user } = req;
    const tracksQuery = await query(
      `
        SELECT id, name, uuid
        FROM tracks_for_sharing
        WHERE author_id = $1
        ORDER BY created_at DESC
      `,
      [user.id]
    );
    const tracks = await Promise.all(
      tracksQuery.rows.map(async (track) => {
        return {
          ...track,
          url: await shortenUrl(trackPrefix + '/tracks/' + track.uuid, { userId: user.id }),
        };
      })
    );
    res.send(tracks);
  },
});

export const setSharingUsername = declareHandler({
  func: async (req, res) => {
    const { user } = req;
    const { shareName } = req.body;
    console.log(req.user, shareName);
    await query('UPDATE users SET share_name = $1 WHERE id = $2', [shareName, user.id]);
    res.send({ success: true });
  },
});

export const setSharedTrackName = declareHandler({
  func: async (req, res) => {
    const { id: trackId } = req.params;
    const { name } = req.body;

    await query(
      `
        UPDATE tracks_for_sharing SET name = $1
        WHERE uuid = $2 AND author_id = $3
      `,
      [name, trackId, req.user.id]
    );
    res.send({ success: true });
  },
});

export const getTrack = declareHandler({
  func: async (req, res) => {
    const { trackId } = req.params;
    const response = await query(
      `
      SELECT t.id AS id,
        name,
        band_energy,
        s3_url,
        u.display_name AS author_name,
        u.share_name AS author_share_name,
        t.created_at AS created_at,
        COUNT(s.track_id) AS play_count,
        CASE WHEN p.remixable THEN p.uuid ELSE NULL END AS project_uuid
      FROM tracks_for_sharing t
      INNER JOIN users u ON t.author_id = u.id
      LEFT JOIN shared_tracks_plays s ON t.id = s.track_id
      LEFT JOIN projects p ON t.project_id = p.id
      WHERE t.uuid = $1
      GROUP BY t.uuid, t.id, u.display_name, u.share_name, p.uuid, p.remixable
    `,
      [trackId]
    );
    const result = response.rows?.[0];
    if (!result) {
      res.send({ error: 'Track not found' });
    }
    res.send(result);
  },
});

export const getTrackPlays = declareHandler({
  func: async (req, res) => {
    let { trackId, userId } = req.body;
    if (!userId) userId = null; // userId defaults to 0 if not present
    await query(
      `
      INSERT INTO shared_tracks_plays (user_id, track_id)
      SELECT $1, t.id
      FROM tracks_for_sharing t
      WHERE t.uuid = $2
    `,
      [userId, trackId]
    );
    res.send({ success: true });
  },
});
