import express from "express";
import { requireAuthenticated } from "../middleware/authority.js";
import { needDatabase } from "../middleware/database.js";
import matchModel from "../schemas/matchModel.js";
import sportModel from "../schemas/sportModel.js";
import userModel from "../schemas/userModel.js";
const MatchController = express.Router();

MatchController.get("/search/:sport", needDatabase, async (req, res) => {
    try {
        let sport = sportModel.findByName(req.params.sport);
        let query = matchModel.find({ sport: sport._id });
        query.where("when").gte(Date.now); // We don't want to return any results of matches that have already occurred.
        if (req.session.userId) query.where("publicity").gte(1).where("friends").in(req.session.userId);
        if (req.query.within) query.where("location").within({ center: req.query.location.split(","), radius: req.query.within });
        if (req.query.minDifficulty) query.where("difficulty").gte(req.query.minDifficulty);
        if (req.query.maxDifficulty) query.where("difficulty").lte(req.query.maxDifficulty);
        if (req.query.beforeDate) query.where("when").lte(req.query.beforeDate);

        let queryResults = await query;
        res.send({ results: queryResults });
    } catch (error) {
        console.error(error);
        res.status(500).send("Internal server error.");
    }
});

MatchController.get("/recent/:limit?", needDatabase, async (req, res) => {
    try {
        let user = null;
        if (req.session.userId) {
            user = await userModel.findById(req.session.userId);
        }
        let limit = parseInt(req.params.limit);
        if (!req.params.limit) limit = 10;
        if (isNaN(limit)) {
            console.log(typeof (limit));
            res.status(400).send("Limit parameter is not a number.");
            return;
        }
        if (isNaN(limit)) {
            res.status(400).send("Limit parameter not a number.");
            return;
        }
        if (limit > 50) {
            res.status(400).send("Limit greater than maximum limit of 50.");
            return;
        }
        let recent = null;
        if (user) {
            recent = matchModel.find({ creator: user._id });
        } else {
            recent = matchModel.find().where("publicity").gte(2);
        }
        recent = await recent.sort({ createDate: -1 }).limit(limit).populate(["sport", "participants"]);
        res.status(200).send({ recent: recent });
    } catch (error) {
        console.error(error);
        res.status(500).send("Internal server error.");
        // TODO: Check and improve error handling.
    }
});

MatchController.post("/", needDatabase, requireAuthenticated, async (req, res) => {
    try {
        const userId = req.session.userId;
        const user = await userModel.findById(userId);
        const match = new matchModel({
            title: req.body.title,
            when: req.body.when,
            public: req.body.public,
            location: req.body.location,
            creator: userId,
            difficulty: req.body.difficulty,
            sport: await sportModel.findByName(req.body.sport),
            participants: [user._id]
        });
        if (!match.sport) {
            res.status(400).send("Invalid sport name provided.");
            return;
        }
        await match.save();
        user.createdMatches.push(match._id);
        user.participatingMatches.push(match._id);
        await user.save();
        res.status(201).send({ createdMatch: match });
    } catch (error) {
        console.error(error);
        res.status(500).send("Internal server error.");
        // TODO: Develop the error handling.
    }
});

MatchController.patch("/:id", needDatabase, requireAuthenticated, async (req, res) => {
    try {
        const match = await matchModel.findById(req.params.id);
        if (!match) {
            res.status(400).send("Invalid match ID provided.");
            return;
        }

        if (req.user._id !== match.creator && req.user.accessLevel < 3) {
            res.status(401).send("Not authorized.");
            return;
        }

        if (req.body._id) {
            res.status(400).send("Cannot change ID of match.");
            return;
        }

        if (req.body.creator) {
            res.status(400).send("Cannot change creator of match.");
            return;
        }
        await match.updateOne(req.body);
        res.status(200).send({ updatedMatch: match });

    } catch (error) {
        res.status(200).send("Internal server error.");
    }
});

MatchController.delete("/:id", needDatabase, requireAuthenticated, async (req, res) => {
    try {
        const match = await matchModel.findById(req.params.id);
        if (!match) {
            res.status(400).send("Invalid match ID provided.");
            return;
        }
        if (req.user._id !== match.creator && req.user.accessLevel < 3) {
            res.status(401).send("Not authorized.");
            return;
        }
        await match.deleteOne();
        res.status(200).send("Deleted.");
    } catch (error) {
        console.error(error);
        res.status(500).send("Internal server error");
    }
});

MatchController.get("/:id", needDatabase, async (req, res) => {
    try {
        if (!req.params.id) {
            res.status(404).send("Id must be provided to retrieve match");
            return;
        }
        const match = await matchModel.findById(req.params.id).populate("sport");
        if (match) {
            res.status(200).send({ match: match });
        } else {
            res.status(404).send("Could not find match with ID: " + req.params.id);
        }
    } catch (error) {
        console.error(error);
        res.status(500).send("Internal server error.");
        // TODO: Improve the error handling.
    }
});

MatchController.get("/join/:id", needDatabase, requireAuthenticated, async (req, res) => {
    try {
        const match = await matchModel.findById(req.params.id);
        const user = req.user;
        if (!match) {
            res.status(400).send("Invalid match ID provided.");
            return;
        }

        if (user.participatingMatches.includes(match._id)) {
            res.status(400).send("Already participating in match.");
            return;
        }

        match.participants.push(user._id);
        user.participatingMatches.push(match._id);

        await match.save();
        await user.save();

        res.status(200).send("Joined.");
    } catch (error) {
        console.error(error);
        res.status(500).send("Internal server error.");
    }
});

MatchController.get("/leave/:id", needDatabase, requireAuthenticated, async (req, res) => {
    try {
        const match = await matchModel.findById(req.params.id);
        const user = req.user;

        if (!match) {
            res.status(400).send("Invalid match ID provided.");
            return;
        }

        if (!user.participatingMatches.includes(match._id)) {
            res.status(400).send("Not part of match.");
            return;
        }

        const userIndex = match.participants.indexOf(user._id);
        match.participants.splice(userIndex, 1);
        await match.save();

        const matchIndex = user.participatingMatches.indexOf(match._id);
        user.participatingMatches.splice(matchIndex, 1);
        await user.save();

        res.status(200).send("Left match.");
    } catch (error) {
        console.error(error);
        res.status(500).send("Internal server error.");
    }
});

export default MatchController;