/*
 * This file is a part of the SteamWar software.
 *
 * Copyright (C) 2023  SteamWar.de-Serverteam
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.sql;

import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.List;
import java.util.stream.Collectors;

@AllArgsConstructor
public class Tutorial {

    private static final Table<Tutorial> table = new Table<>(Tutorial.class);
    private static final SelectStatement<Tutorial> by_popularity = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.Released = ? GROUP BY t.TutorialID ORDER BY SUM(r.Stars) DESC LIMIT ?, ?");
    private static final SelectStatement<Tutorial> own = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.Creator = ? GROUP BY t.TutorialID ORDER BY t.TutorialID ASC LIMIT ?, ?");
    private static final SelectStatement<Tutorial> by_creator_name = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.Creator = ? AND t.Name = ? GROUP BY t.TutorialID");
    private static final SelectStatement<Tutorial> by_id = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.TutorialID = ? GROUP BY t.TutorialID");
    private static final Statement rate = new Statement("INSERT INTO TutorialRating (TutorialID, UserID, Stars) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE Stars = VALUES(Stars)");
    private static final Statement create = new Statement("INSERT INTO Tutorial (Creator, Name, Item) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE Item = VALUES(Item), Released = 0");
    private static final Statement release = table.update(Table.PRIMARY, "released");
    private static final Statement delete = table.delete(Table.PRIMARY);

    public static List<Tutorial> getPage(int page, int elementsPerPage, boolean released) {
        List<Tutorial> tutorials = by_popularity.listSelect(released, page * elementsPerPage, elementsPerPage);
        SteamwarUser.batchCache(tutorials.stream().map(tutorial -> tutorial.creator).collect(Collectors.toSet()));
        return tutorials;
    }

    public static List<Tutorial> getOwn(int user, int page, int elementsPerPage) {
        return own.listSelect(user, page * elementsPerPage, elementsPerPage);
    }

    public static Tutorial create(int creator, String name, String item) {
        create.update(creator, name, item);
        return by_creator_name.select(creator, name);
    }

    public static Tutorial get(int id) {
        return by_id.select(id);
    }

    @Getter
    @Field(keys = {Table.PRIMARY}, autoincrement = true)
    private final int tutorialId;
    @Getter
    @Field(keys = {"CreatorName"})
    private final int creator;
    @Getter
    @Field(keys = {"CreatorName"})
    private final String name;
    @Getter
    @Field(def = "'BOOK'")
    private final String item;
    @Getter
    @Field(def = "0")
    private final boolean released;
    @Getter
    @Field(def = "0") // Not really a field, but necessary for select generation
    private final double stars;

    public void release() {
        release.update(1, tutorialId);
    }

    public void delete() {
        delete.update(tutorialId);
    }

    public void rate(int user, int rating) {
        rate.update(tutorialId, user, rating);
    }
}
