const express = require("express");
const fs = require("fs");
const session = require("express-session");
const fileUpload = require("express-fileupload");
const app = express();
const fuzzball = require("fuzzball");
const mysql = require('mysql');

const connection = mysql.createConnection({
  host: 'localhost',
  user: 'music', // замените на свой логин
  password: 'password', // замените на свой пароль
  database: 'music' // замените на свою базу данных
});

connection.connect((err) => {
  if (err) {
    console.error('Ошибка подключения к базе данных: ', err);
  } else {
    console.log('Подключение к базе данных успешно');
  }
});

app.set("view engine", "ejs");
app.use(express.static("public"));

app.use(express.urlencoded({ extended: true }));
app.use(fileUpload());
app.use(session({
  secret: "mysecretkey",
  resave: false,
  saveUninitialized: false
}));

const citiesAndRegions = JSON.parse(fs.readFileSync("./db/russia.json", "utf8"));


//const predefinedGenres = ['Rock', 'Pop', 'Jazz', 'Hip Hop', 'Electronic', 'Blues'];

// Функция для получения последних музыкантов, зарегистрированных музыкантов
function getLastNRegisteredMusicians(N, callback) {
  connection.query("SELECT * FROM users ORDER BY id DESC LIMIT ?", [N], (err, result) => {
    if (err) {
      console.error("Ошибка при получении последних зарегистрированных музыкантов: ", err);
      return callback(err);
    } else {
      return callback(null, result);
    }
  });
}

function getMusicians(callback) {
  connection.query("SELECT * FROM users", (err, result) => {
    if (err) {
      console.error("Ошибка при получении списка музыкантов: ", err);
      return callback(err);
    } else {
      return callback(null, result);
    }
  });
}

// Функция для получения музыканта по id
function getMusicianById(id, callback) {
  connection.query("SELECT * FROM users WHERE id=?", [id], (err, result) => {
    if (err) {
      console.error("Ошибка при получении музыканта с id ${id}: ", err);
      return callback(err);
    } else {
      return callback(null, result[0]);
    }
  });
}

function requireLogin(req, res, next) {
  if (req.session.musicianId) {
    next();
  } else {
    res.redirect("/login");
  }
}

//search function
function search(query = '', role = '', city = '', genre = '') {
  let results = [];

  if (query || role || city || genre) {
    const queryValues = [
      `%${query.toLowerCase()}%`,
      role === '' ? null : role,
      city === '' ? null : city.toLowerCase().trim(),
      genre === '' ? null : genre.toLowerCase()
    ];

    const queryConditions = [
      'name LIKE ? OR genre LIKE ?',
      'role = ?',
      'city = ?',
      'genre = ?'
    ].filter(condition => queryValues[queryConditions.indexOf(condition)] !== null);

    const sql = `SELECT * FROM users WHERE ${queryConditions.join(' AND ')}`;

    connection.query(sql, queryValues, (err, result) => {
      if (err) {
        console.error('Ошибка при поиске: ', err);
        return results;
      } else {
        results = result.map(musician => {
          return {
            name: musician.name,
            genre: musician.genre,
            originalName: musician.name,
            profileLink: `/profile/${musician.id}`,
            thumbnail: musician.thumbnail,
            soundcloud: musician.soundcloud,
            role: musician.role,
            city: musician.city
          };
        }).sort((a, b) => {
          const aNameScore = a.name.toLowerCase().startsWith(query) ? 2 : a.name.toLowerCase().includes(query) ? 1 : 0;
          const bNameScore = b.name.toLowerCase().startsWith(query) ? 2 : b.name.toLowerCase().includes(query) ? 1 : 0;
          const aGenreScore = a.genre.toLowerCase().startsWith(query) ? 2 : a.genre.toLowerCase().includes(query) ? 1 : 0;
          const bGenreScore = b.genre.toLowerCase().startsWith(query) ? 2 : b.genre.toLowerCase().includes(query) ? 1 : 0;

          // Sort by name score, then genre score, then location score (descending)
          if (aNameScore + aGenreScore + a.city < bNameScore + bGenreScore + b.city) {
            return 1;
          } else if (aNameScore + aGenreScore + a.city > bNameScore + bGenreScore + b.city) {
            return -1;
          } else {
            return 0;
          }
        });

        // Remove duplicates
        results = results.filter((result, index, self) =>
          index === self.findIndex(r => (
            r.name === result.name && r.genre === result.genre && r.city === result.city
          ))
        );

        return results;
      }
    });
  }
}

app.use((req, res, next) => {
  if (req.session.musicianId) {
    getMusicianById(req.session.musicianId, (err, musician) => {
      if (err) {
        console.error("Ошибка при получении current user: ", err);
      } else {
        res.locals.musician = musician;
        res.locals.userLoggedIn = true;
        res.locals.username = musician.name;
      }
    });
  } else {
    res.locals.userLoggedIn = false;
  }
  next();
});

app.get("/", (req, res) => {
getLastNRegisteredMusicians(5, (err, lastRegisteredMusicians) => {
if (err) {
console.error("Ошибка при получении последних зарегистрированных музыкантов: ", err);
res.status(500).send("Ошибка получения данных");
} else {
res.render("index", { musicians: lastRegisteredMusicians, query:'',city:'',role:'',genre:'' });
}
});
});

app.get("/autocomplete/cities", async (req, res) => {
const searchString = req.query.term;

connection.query(
"SELECT city FROM users WHERE city LIKE ?",
[searchString + '%'],
(error, results, fields) => {
if (error) {
console.error("Ошибка выполнения запроса: ", error);
res.status(500).send("Ошибка выполнения запроса");
} else {
const cities = results.map(row => row.city);
res.json(cities);
}
}
);
});

app.get("/register", (req, res) => {
  if (req.session.musicianId) {
    const musician = getMusicianById(req.session.musicianId);
    res.redirect("/profile/" + musician.id);
  } else {
	  
	  
	  
    res.render("register", { citiesAndRegions, city:'', query:'',role:'' });
  }
});

app.post("/register", (req, res) => {
  if (req.session.musicianId) {
    getMusicianById(req.session.musicianId, (err, musician) => {
      if (err) {
        console.error("Ошибка при получении музыканта: ", err);
      } else {
        res.redirect("/profile/" + musician.id);
      }
    });
  } else {
    const newMusician = {
      name: req.body.name,
      genre: req.body.genre,
      instrument: req.body.instrument,
      soundcloud: req.body.soundcloud,
      password: req.body.password,
      role: req.body.role,
      city: req.body.city,
      login: req.body.login,
    };

    if (req.files && req.files.thumbnail) {
      const file = req.files.thumbnail;
      const filename = "musician_" + file.name;
      file.mv("./public/img/" + filename);
      newMusician.thumbnail = filename;
    }

    connection.query("INSERT INTO users SET ?", newMusician, (err, result) => {
      if (err) {
        console.error("Ошибка при регистрации нового музыканта: ", err);
        res.status(500).send("Ошибка регистрации");
      } else {
        req.session.musicianId = result.insertId;
        res.redirect("/profile/" + result.insertId);
      }
    });
  }
});

app.get("/profile/:id", (req, res) => {
  const musician = getMusicianById(parseInt(req.params.id));

  if (musician) {
    res.render("profile", { musician: musician, city:'', query:'', role:'' });
  } else {
    res.status(404).send("Musician not found");
  }
});

app.get("/login", (req, res) => {
  res.render("login");
});

app.post("/login", (req, res) => {
  connection.query("SELECT * FROM users WHERE login=? AND password=?", [req.body.login, req.body.password], (err, result) => {
      if (err) {
        console.error("Ошибка при входе: ", err);
        res.status(500).send("Ошибка при входе");
      } else {
        if (result.length > 0) {
          req.session.musicianId = result[0].id;
          res.redirect("/profile/" + result[0].id);
        } else {
          res.render("login", { error: "Invalid login or password" });
        }
      }
    }
);
});

app.get("/logout", (req, res) => {
  req.session.destroy();
  res.redirect("/");
});

app.get('/search', (req, res) => {
  const query = req.query.query || '';
  const role = req.query.role || '';
  const city = req.query.city || '';
  const genre = req.query.genre || '';
  
  let musicians = [];

  if (query || role || city || genre) {
    musicians = search(query, role, city, genre);
  } else {
    const data = fs.readFileSync('./db/musicians.json');
    musicians = JSON.parse(data).musicians.map(musician => {
      return {
        name: musician.name,
        genre: musician.genre,
        originalName: musician.name,
        profileLink: `/profile/${musician.id}`,
        thumbnail: musician.thumbnail,
        soundcloud: musician.soundcloud,
        role: musician.role,
		city: musician.city
      };
    });
  }

  //res.locals.predefinedGenres = predefinedGenres;
  app.locals.JSON = JSON;
  res.render('search', { musicians, query, role, city, genre, citiesAndRegions});

  //res.redirect('/search');
});


app.get("/profile/:id/edit", requireLogin, (req, res) => {
  const musician = getMusicianById(parseInt(req.params.id));

  if (musician) {
    if (req.session.musicianId === musician.id) { // Check if the logged-in user is the owner of the profile
      res.render("edit-profile", { musician: musician });
    } else {
      res.status(403).send("Access denied");
    }
  } else {
    res.status(404).send("Musician not found");
  }
});





app.post("/profile/:id/edit", requireLogin, (req, res) => {
  const musicianId = parseInt(req.params.id);

  getMusicianById(musicianId, (err, musician) => {
    if (err) {
      console.error("Ошибка при получении музыканта: ", err);
      res.status(500).send("Ошибка при получении данных");
    } else {
      if (musician) {
        const updatedMusician = {
          name: req.body.name,
          genre: req.body.genre,
          instrument: req.body.instrument,
          soundcloud: req.body.soundcloud,
          soundcloud1: req.body.soundcloud1,
          soundcloud2: req.body.soundcloud2,
          city: req.body.city,
          role: req.body.role,
          bio: req.body.bio,
        };

        if (req.files && req.files.thumbnail) {
          const file = req.files.thumbnail;
          const filename = "musician_" + musicianId + "_" + file.name;
          file.mv("./public/img/" + filename);
          updatedMusician.thumbnail = filename;
        }

        connection.query("UPDATE users SET ? WHERE id=?", [updatedMusician, musicianId], (err, result) => {
          if (err) {
            console.error("Ошибка при обновлении профиля музыканта: ", err);
            res.status(500).send("Ошибка при обновлении профиля");
          } else {
            res.redirect("/profile/" + musicianId);
          }
        });
      } else {
        res.status(404).send("Musician not found");
      }
    }
  });
});

function isValidSoundCloudUrl(url) {
  return url.startsWith('https://soundcloud.com/');
}

app.listen(3000, () => {
  console.log("Server started on port 3000");
});

C:\Users\Ilya\Downloads\my-musician-network>node app.js
Server started on port 3000
Подключение к базе данных успешно
C:\Users\Ilya\Downloads\my-musician-network\node_modules\mysql\lib\protocol\Parser.js:437
      throw err; // Rethrow non-MySQL errors
      ^

TypeError: callback is not a function
    at Query.<anonymous> (C:\Users\Ilya\Downloads\my-musician-network\app.js:70:14)

Также, надо перевести остатки кода (например, app.get search), где есть чтение из musicians.json на sql, как я это сделал почти во всем коде